﻿using Devil;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameToolkit
{
    /// <summary>
    /// 无序数组
    /// 该数据结构以数组的形式存储数据，但对在添加或删除时改变数组的序列，
    /// 以满足那些不关心数据顺序，但希望快速添加删除或查询的操作。
    /// </summary>
    /// <typeparam name="T"></typeparam>
	public class RandomList<T> : ICollection<T>
    {
        T[] mValues;
        int mCount;
        bool mIsStruct;
        EquatableDelegate<T> mEqualtable;
        public bool IsReadOnly { get { return false; } }

        public RandomList(int capacity = 32, EquatableDelegate<T> equaltablePattern = null, bool alwaysAsValueType = false)
        {
            mValues = new T[capacity];
            mIsStruct = alwaysAsValueType || typeof(T).IsValueType;
            if (equaltablePattern == null)
                mEqualtable = IsEqual;
            else
                mEqualtable = equaltablePattern;
        }

        public RandomList(RandomList<T> other, int initSize = 4)
        {
            mValues = new T[other.mCount > initSize ? other.mCount : initSize];
            mIsStruct = other.mIsStruct;
            mEqualtable = other.mEqualtable == other.IsEqual ? IsEqual : other.mEqualtable;
            mCount = other.mCount;
            for (int i = 0; i < other.mCount; i++)
            {
                mValues[i] = other.mValues[i];
            }
        }

        public int Count { get { return mCount; } }

        public EquatableDelegate<T> EqualtablePattern
        {
            get { return mEqualtable; }
            set
            {
                if (value == null)
                    mEqualtable = IsEqual;
                else
                    mEqualtable = value;
            }
        }

        protected virtual bool IsEqual(T a, T b)
        {
            if (a is System.IEquatable<T>)
                return ((System.IEquatable<T>)a).Equals(b);
            else if (mIsStruct || a != null)
                return a.Equals(b);
            else
                return b == null;
        }

        private void EnsureCapacity(int count)
        {
            if (mValues.Length < count)
            {
                var len = Mathf.Max(count + 5, mValues.Length + (mValues.Length >> 1));
                var values = new T[len];
                System.Array.Copy(mValues, 0, values, 0, mCount);
                mValues = values;
            }
        }

        public void CopyTo(T[] array, int off)
        {
            var size = Mathf.Min(array.Length - off, mCount);
            System.Array.Copy(mValues, 0, array, off, size);
        }

        public void CopyTo(ICollection<T> collection)
        {
            if (collection != null && collection != this)
            {
                for (int i = mCount - 1; i >= 0; i--)
                {
                    collection.Add(mValues[i]);
                }
            }
        }

        public T this[int index]
        {
            get { return mValues[index]; }
            set
            {
                if (index < 0 || index >= mCount)
                    throw new System.IndexOutOfRangeException();
                mValues[index] = value;
            }
        }

        public void Sort(System.Comparison<T> comparison)
        {
            for (int i = mCount - 1; i > 0; i--)
            {
                for (int k = 0; k < i; k++)
                {
                    var compare = comparison(mValues[i], mValues[k]);
                    if (compare < 0)
                    {
                        var v = mValues[k];
                        mValues[k] = mValues[i];
                        mValues[i] = v;
                    }
                }
            }
        }

        public void MakeRandomList(int start, int end)
        {
            T tmp;
            var count = end > mCount ? (mCount - start) : (end - start);
            for (int i = 1; i < count; i++)
            {
                var index = i - 1 + start;
                var r = Mathf.RoundToInt(ParallelUnity.GetRandomValue() * (count - i)) + index;
                if (r > index && r < end)
                {
                    tmp = mValues[r];
                    mValues[r] = mValues[index];
                    mValues[index] = tmp;
                }
            }
        }

        public void Add(T value)
        {
            EnsureCapacity(mCount + 1);
            mValues[mCount++] = value;
        }

        public void AddRange(ICollection<T> collection)
        {
            var len = collection == null ? 0 : collection.Count;
            if (len > 0)
            {
                EnsureCapacity(mCount + len);
                foreach (var t in collection)
                {
                    mValues[mCount++] = t;
                }
            }
        }

        public int IndexOf(T value)
        {
            for (int i = 0; i < mCount; i++)
            {
                if (mEqualtable(mValues[i], value))
                    return i;
            }
            return -1;
        }

        public bool Contains(T value)
        {
            for (int i = 0; i < mCount; i++)
            {
                if (mEqualtable(mValues[i], value))
                    return true;
            }
            return false;
        }

        public T RemoveAt(int index)
        {
            if (index >= mCount || index < 0)
                throw new System.IndexOutOfRangeException();
            var v = mValues[index];
            mCount--;
            if (index < mCount)
            {
                mValues[index] = mValues[mCount];
                mValues[mCount] = default;
            }
            else
            {
                mValues[index] = default;
            }
            return v;
        }

        public bool Remove(T value)
        {
            for (int i = 0; i < mCount; i++)
            {
                if (mEqualtable(mValues[i], value))
                {
                    RemoveAt(i);
                    return true;
                }
            }
            return false;
        }

        public void RemoveAll(T value)
        {
            for (int i = mCount - 1; i >= 0; i--)
            {
                if (mEqualtable(mValues[i], value))
                    RemoveAt(i);
            }
        }

        public void Clear()
        {
            if (!mIsStruct)
            {
                for (int i = 0; i < mCount; i++)
                {
                    mValues[i] = default(T);
                }
            }
            mCount = 0;
        }

        public void Cleanup()
        {
            for (int i = 0; i < mCount; i++)
            {
                mValues[i] = default(T);
            }
            mCount = 0;
        }

        //public void FreeMemery(int cacheSize)
        //{
        //    var num = Mathf.Min(mCount + cacheSize, mValues.Length);
        //    if(num != mValues.Length)
        //    {
        //        var arr = new a
        //    }
        //}

        public IEnumerator<T> GetEnumerator()
        {
            return new SliceEnumerator<T>(mValues, 0, mCount);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new SliceEnumerator<T>(mValues, 0, mCount);
        }

    }
}